<?php
/**
 * Copyright (c) 2006-2007, Julien PORTALIER
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright    Copyright (c) 2006-2007, Julien PORTALIER
 * @link         http://featherphp.googlecode.com/
 * @license      http://www.opensource.org/licenses/mit-license.php The MIT License
 */

/**
 * SQLite backend.
 */
class db_sqlite extends db_base
{
	protected $operators = '=|<|>|<=|>=|<>|<=>|like|regexp';
	protected $fieldQuote = '"';
	
	// ignition
	
	protected function __start()
	{
		$this->useDatabase($this->conf['database']);
	}
	
	function __stop()
	{
		sqlite_close($this->link);
		$this->link = false;
	}
	
	function useDatabase($database=null)
	{
		if (empty($database))
			$database = $this->conf['database'];
		$connect = "sqlite_".(empty($this->conf['permanent']) ? 'open' : 'popen');
		$this->link = $connect($this->conf['database'], 0664) or $this->error();
	}
	
	// SQL
	
	function transactions($com)
	{
		switch($com)
		{
			case 'start':    return $this->execute('BEGIN TRANSACTION ;');
			case 'commit':   return $this->execute('COMMIT ;');
			case 'rollback': return $this->execute('ROLLBACK ;');
		}
	}
	
	function execute($sql)
	{
		if (!$this->link)
			$this->__start();
		
		# execute & time request
		$_start = microtime(true);
		$rs     = sqlite_exec($this->link, $sql);
		$_end   = microtime(true);
		
		# error?
		if (!$rs)
			$this->error($sql);
		else if (DEBUG < 2)
		{
			# slow request?
			if ($_end - $_start > 1.0)
				file_log(sprintf('%.2fs : %s', $_end - $_start, $sql), 'db_sqlite_slow_queries');
		}
		else
		{
			# debug
			$this->debug($sql, $_end - $_start);
		}
		
		return $rs;
	}
	
	protected function & __query($sql, $type=SQLITE_ASSOC)
	{
		if (!$this->link)
			$this->__start();
		
		# execute & time request
		$_start = microtime(true);
		$rs     = sqlite_array_query($this->link, $sql, $type);
		$_end   = microtime(true);
		
		# error?
		if (!$rs and !is_array($rs))
			$this->error($sql);
		else if (DEBUG < 2)
		{
			# slow request?
			if ($_end - $_start > 1.0)
				file_log(sprintf('%.2fs : %s', $_end - $_start, $sql), 'db_sqlite_slow_queries');
		}
		else
		{
			# debug
			$this->debug($sql, $_end - $_start);
		}
		
		return $rs;
	}
	
	/**
	 * Queries SQL server for data.
	 * 
	 * Returns data in 3 possible forms, depending on $process.
	 * Defaults to Q_PROCESS_FULL.
	 * 
	 * Q_PROCESS_FULL:
	 *   Useful when calling data from multiple tables at once.
	 *   Format: $data[table][field] = values
	 * 
	 * Q_PROCESS_LITE:
	 *   Useful when calling data from a single table.
	 *   Format: $data[field] = values
	 * 
	 * Q_PROCESS_NONE:
	 *   No processing is done.
	 *   Format: $data[] = values
	 * 
	 * Q_PROCESS_LIST:
	 *   Lists a single value (eg: a list of tags).
	 *   Format: $data = array(value, value, value)
	 * 
	 * Q_PROCESS_ASSOC_LIST:  
	 *   Lists a pair of values (eg: useful for HTML's select).
	 *   Format: $data = array(key => value, key => value)
	 */
	function & select($sql, $process=Q_PROCESS_FULL, $tableName=0)
	{
		# query
		if (is_array($sql))
			$sql = $this->prepareSelect($sql);
		
		# data
		$data = array();
		switch($process)
		{
			case Q_PROCESS_NONE:
				$data = $this->__query($sql, SQLITE_NUM);
				break;
			
			# data[table][field] = value
			case Q_PROCESS_FULL:
				$rs = $this->__query($sql);
				foreach($rs as $row)
				{
					$d = array();
					foreach($row as $field => $v)
					{
						$field = str_replace('"', '', $field);
						if (strpos($field, '.') !== false)
							list($table, $field) = explode('.', $field);
						else
							$table = $tableName;
						$d[$table][$field] = $v;
					}
					array_push($data, $d);
				}
				break;
			
			# data[field] = value
			case Q_PROCESS_LITE:
				$rs = $this->__query($sql);
				foreach($rs as $row)
				{
					$d = array();
					foreach($row as $field => $v)
					{
						list($table, $field) = explode('.', $field);
						$d[$field] = $v;
					}
					array_push($data, $d);
				}
				break;
			
			# data[index] = value
			case Q_PROCESS_LIST:
				$rs = $this->__query($sql, SQLITE_NUM);
				foreach($rs as $row)
					array_push($data, $row[0]);
				break;
			
			# data[key] = value
			case Q_PROCESS_ASSOC_LIST:
				$rs = $this->__query($sql, SQLITE_NUM);
				foreach($rs as $row)
					$data[$row[0]] = $row[1];
				break;
			
			# data[] = value
			default:
				$data = $rs;
		}
		
		return $data;
	}
	
	function & describeTable($table)
	{
		$fields = array();
		
		$rs = $this->__query("PRAGMA table_info($table) ;");
		$fields = array();
		foreach ($rs as $f)
		{
			$fields[$f['name']] = array(
				'type' =>  $f['type'],
				'null' => !$f['notnull'],
				'key'  => ($f['pk'] == 1)
			);
			
			# FIXME Get field's length!
			
			// ...
		}
		return $fields;
	}
	
	// data
	
	function limit($limit, $page=1)
	{
		if ($page > 1)
			return "LIMIT $limit OFFSET ".(($page - 1) * $limit);
		elseif ($limit)
			return "LIMIT $limit";
	}
	/*
	function field($f)
	{
		// do not process field?
		if (strpos($f, '-! ') === 0)
			return str_replace('-! ', '', $f);
		
		// operator
		if (stripos($f, 'DISTINCT') !== false)
		{
			$op = 'DISTINCT';
			$f = str_ireplace('DISTINCT', '', $f);
		}
		else {
			$op = '';
		}
		$f = trim($f);
		
		if (strpos($f, '.'))
		{
			// model.field type
			list($m, $f) = explode('.', $f);
			$f = ($f != '*') ? "\"$m\".\"$f\"" : "\"$m\".$f";
		}
		else
		{
			// simple field
			$f = ($f != '*') ? "\"$f\"" : $f;
		}
		
		return "$op $f";
	}
	*/
	function value($v)
	{
		if (!$this->link)
			$this->__start();
		if (strpos($v, '-! ') === 0)
		{
			// do not process value
			return str_replace('-! ', '', $v);
		}
		elseif ($v === true)
			return 1;
		elseif ($v === false)
			return 0;
#		elseif (!is_numeric($v))
#		{
			// string or numeric
			if (empty($v) and strlen($v) == 0)
				return 'NULL';
#			$v = str_replace(CRLF, LF, $v);
			$v = sqlite_escape_string($v);
			return "'$v'";
#		}
		return $v;
	}
	
	function getLastInsertId()
	{
		return sqlite_last_insert_rowid($this->link);
	}
	
	// debug
	
	protected function error($sql=null)
	{
		$code = sqlite_last_error($this->link);
		
		if (DEBUG)
		{
			if (!is_null($sql))
				debug($sql);
			die("SQLite error: ".sqlite_error_string($code));
		}
		
		# log
		file_log(sqlite_error_string($code), 'db_sqlite_errors');
		Error::show(500);
	}
	
	protected function debug($sql, $time=null)
	{
		$code = sqlite_last_error($this->link);
		$q = array(
			'sql'      => $sql,
			'error'    => ($code == 0) ? '-' : sqlite_error_string($code),
			'affected' => sqlite_changes($this->link),
			'time'     => $time
		);
		array_push($this->debugs, $q);
	}
}
?>